﻿using System;
using biz.ritter.javapi.lang;
using biz.ritter.javapi.util;
using biz.ritter.javapi.util.MapNS;
using Nofs.Net.Common.Interfaces.Cache;
using Nofs.Net.Common.Interfaces.Domain;
using Nofs.Net.Common.Interfaces.Library;
using Db4objects.Db4o;


namespace Nofs.Net.nofs.Db4o
{
    public class KeyCache : IKeyCache
    {
        private Map<Integer, IKeyIdentifier> _refCache;
        private bool _initRef;
        private IObjectContainer _db;

        public KeyCache(IObjectContainer db)
        {
            _initRef = false;
            _refCache = new TreeMap<Integer, IKeyIdentifier>();
            _db = db;
        }

        public int CacheSize()
        {
            InitRefCache();
            return _refCache.size();
        }

        //@SuppressWarnings("unchecked")
        private void InitRefCache()
        {
            if (!_initRef)
            {
                foreach (KeyIdentifier key in _db.Query(typeof(KeyIdentifier)))
                {
                    if (!(key.getReference() is IObjectWithID))
                    {
                        _refCache.put(new Integer(key.getReference().hashCode()), key);
                    }
                }
                _initRef = true;
            }
        }

        //@SuppressWarnings({ "unchecked" })
        public IKeyIdentifier GetByID(UUID id) //throws Exception 
        {
            IObjectSet result = _db.QueryByExample(new KeyIdentifier(id, null));
            if (result.Count != 1)
            {
                throw new System.Exception("getting key identifier ID = " + id.toString() + " returned " + result.Count + " results ");
            }

            return (KeyIdentifier)result.Ext().Get(0);
        }


        //@SuppressWarnings("unchecked")
        public IKeyIdentifier GetByReference(Object reference) //throws Exception 
        {
            if (reference is IObjectWithID)
            {
                return GetByID(((IObjectWithID)reference).getID());
            }
            InitRefCache();
            IKeyIdentifier keyId = _refCache.get(reference.hashcode());
            if (keyId == null)
            {
                Entry<Integer, IKeyIdentifier> entry = null;

                Entry<Integer, IKeyIdentifier>[] array = new Entry<Integer, IKeyIdentifier>[_refCache.entrySet().size()];
                _refCache.entrySet().toArray<Entry<Integer, IKeyIdentifier>>(array);

                foreach (Entry<Integer, IKeyIdentifier> candidate in array)
                {
                    if (((KeyIdentifier)candidate.getValue()).getReference() == reference)
                    {
                        entry = candidate;
                        break;
                    }
                }
                if (entry == null)
                {
                    throw new System.Exception("could not find key by reference");
                }
                _refCache.remove(entry.getKey());
                _refCache.put(entry.getValue().hashCode(), entry.getValue());
                keyId = entry.getValue();

            }
            return keyId;
        }

        //@SuppressWarnings("unchecked")
        public void Add(IKeyIdentifier key)
        {
            KeyIdentifier id = (KeyIdentifier)key;
            _refCache.put(id.getReference().hashCode(), id);
            _db.Store(id);
            _db.Commit();
        }

        //@SuppressWarnings("unchecked")
        public void Remove(IKeyIdentifier key) //throws Exception 
        {
            KeyIdentifier id = (KeyIdentifier)key;
            _refCache.remove(id.getReference().hashCode());
            _db.Delete(id);
            _db.Commit();
        }

        public void Remove(Object reference) //throws Exception
        {
            Remove(GetByReference(reference));
        }
    }

}
